package com.gitee.loyo;

import com.gitee.loyo.config.properties.ChargeProperties;
import com.gitee.loyo.config.properties.ChargeQueryProperty;
import com.gitee.loyo.data.Token;
import com.gitee.loyo.err.RequestFailureException;
import com.gitee.loyo.utils.OkHttpHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Configuration
@ComponentScan
@EnableConfigurationProperties({ChargeProperties.class})
@RequiredArgsConstructor
public class ChargeAutoConfiguration {
    private Lock lock = new ReentrantLock();
    final ChargeProperties chargeProperties;
    final OkHttpHelper okHttpHelper;
    final TokenManager tokenManager;

    @Bean
    @ConditionalOnMissingBean
    StationService getStationService() {
        return (StationService) Proxy.newProxyInstance(
                ChargeAutoConfiguration.class.getClassLoader(),
                new Class[]{StationService.class},
                new ChargingInvocationHandler()
        );
    }

    @Bean
    @ConditionalOnMissingBean
    BusinessService getBusinessService() {
        return (BusinessService) Proxy.newProxyInstance(
                ChargeAutoConfiguration.class.getClassLoader(),
                new Class[]{BusinessService.class},
                new ChargingInvocationHandler()
        );
    }

    @RequiredArgsConstructor
    public class ChargingInvocationHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ApiName apiName = method.getAnnotation(ApiName.class);
            Request reqParam = (Request) args[0];
            String operatorKey = (String) args[1];
            ChargeQueryProperty queryProperty = chargeProperties.getQueryProperty(operatorKey);
            String url = builderUrl(queryProperty.getUrl(), apiName.value());
            return okHttpHelper.post(url, reqParam, getToken(operatorKey), method.getGenericReturnType(), queryProperty);
        }

        private String getToken(String operatorKey) throws IOException {
            Token token = tokenManager.getToken(operatorKey);
            if (token == null) {
                try {
                    if (lock.tryLock(5, TimeUnit.SECONDS)) {
                        token = tokenManager.getToken(operatorKey);
                        if (token == null) {
                            token = tokenManager.queryToken(operatorKey);
                            tokenManager.saveToken(token, operatorKey);
                        }
                    }
                } catch (RequestFailureException e) {
                    throw e;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }
            return token.getAccessToken();
        }

        private String builderUrl(String baseUrl, String apiName) {
            return baseUrl.endsWith("/") ? baseUrl + apiName
                    : baseUrl + "/" + apiName;
        }
    }
}
